package org.sjsu.freedoor.model;

import java.net.InetAddress;

import org.json.JSONArray;
import org.json.JSONObject;
import org.sjsu.freedoor.db.DBHandler;
import org.sjsu.freedoor.db.MongoHandler;
import org.sjsu.freedoor.replication.ReplicationThread;

//singleton
public class Freedoor {
	
	public static String SELF_URL = "";
	public final static int NUMBER_OF_DOCUMENTS_PER_PAGE = 10;
	
	public enum RequestReturnType {
		Fail,
		Success,
		IdNotFound,
		MethodNotImplemented
	}
	
	private static Freedoor instance = null;
	private DBHandler db;
	
	private Freedoor() {
		System.out.println("[STARTUP]  Starting FreeDoor..");
		
		//set SELF_URL
	    try
	    {
	       InetAddress thisIp = InetAddress.getLocalHost();
	       SELF_URL = "http://" + thisIp.getHostAddress().toString() + ":8080/FreeDoor";
	       System.out.println("[STARTUP]  FreeDoor services started on " + SELF_URL);
	    }
	    catch(Exception e){}
	    
	    //SELF_URL = "http://192.168.1.3:8088/FreeDoor"; //hardcode override
		
	    //initialize db handler
		db = new MongoHandler();
		
		//initialize replication thread
		ReplicationThread rep = new ReplicationThread();
		rep.start();
		
   }
	
	//	returns the only instance.
	public static Freedoor getInstance() {
	      if(instance == null) {
	          instance = new Freedoor();
	       }
	       return instance;
	}
	
	
	public CommunityList getCommunities() {
		return db.getCommunities();
	}
	
	public boolean postCommunities(String json) {
		//	Parse the JSON string (body) and build an array of services objects.
		try {
			JSONObject rootObject = new JSONObject(json);
			JSONArray services = rootObject.optJSONArray("communities");
			
			
			if(services !=null){ //check if we see any array in the json string
				for (int i = 0; i < services.length(); i++) { // Loop over each each row
					JSONObject community = services.getJSONObject(i); // Get row object
					String desc = community.getString("description");
					String url = community.getString("url");
					
					System.out.println("*** desc = " + desc);
					System.out.println("*** url = " + url);
					
					//	Build a new service object with the parsed data and add it to the list.
					Community c = new Community(desc, url);
					db.postCommunity(c);
				}
			}
			else{ //no array is found, so assume a single member
				String desc = rootObject.getString("description");
				String url = rootObject.getString("url");
				
				System.out.println("*** desc = " + desc);
				System.out.println("*** url = " + url);

				
				//	Build a new service object with the parsed data and add it to the list.
				Community c = new Community(desc, url);
				db.postCommunity(c);
			}
					
		}
		catch(Exception e) {
			e.printStackTrace();
			System.out.println("Unable to parse community from JSON string: " + json);
			return false;
		}
		
		return true;
	}
	
	//	gets the list of all services.
	public ServiceList getServices() {
		return db.getServices(0);
	}
	
	public ServiceList getServices(int pageNumber) {
		return db.getServices(pageNumber);
	}
	
	//	gets the service information of the the given id
	public Service getService(String id) {
		return  db.getService(id);
	}
	
	//puts a collection of services iteratively (inserts if new, updates if <URL, ID> exists)
	//returns CSV of successfully inserted/put IDs
	public String insertOrUpdateServices(ServiceList services) {
		String id = "";
		StringBuilder ids = new StringBuilder();

		for ( Service s : services.getList() ) {
			id = db.putService(s);
			if(!id.isEmpty())
				ids.append(id + ",");
		}
		if(ids.length() > 0)
			ids.deleteCharAt(ids.length()-1);

		return ids.toString();
	}
	
	//updates an existing service if already present
	public String updateService(Service service) {
		return db.updateService(service);
	}
	
	public RequestReturnType removeService(String objectId) {
		return db.removeService(objectId);
	}
	
	//	searches and filters the list of all services bound to the specified name and/or status and page number
	//	key-value example: all services with 'name :' = name  and/or with 'status :' = status etc.
	public ServiceList searchServices(String name, String status, int pagenumber) {
		return db.searchServices(name, status, pagenumber);
	}
	
	//Offers
	public OfferList getOffersForService(String objectId) {
		return db.getOffersForService(objectId);
	}
	
	public String putOffersForService(String objectId, OfferList offers) {
		return db.putOffersForService(objectId, offers);
	}
	
	//TODO: implement only if needed
	public RequestReturnType putOfferForService(String objectId, Offer offer) {
		//return m.putOffer(objectId, offer);
		return RequestReturnType.MethodNotImplemented;
	}
	
//	gets the list of all goods.
	public GoodList getGoods() {
		return db.getGoods(0);
	}
	
	public GoodList getGoods(int pageNumber) {
		return db.getGoods(pageNumber);
	}
	
	//	gets the good information of the the given id
	public Good getGood(String id) {
		return  db.getGood(id);
	}
	
	//puts a collection of goods iteratively (inserts if new, updates if <URL, ID> exists)
	//returns CSV of successfully inserted/put IDs
	public String insertOrUpdateGoods(GoodList goods) {
		String id = "";
		StringBuilder ids = new StringBuilder();

		for ( Good g : goods.getList() ) {
			id = db.putGood(g);
			if(!id.isEmpty())
				ids.append(id + ",");
		}
		if(ids.length() > 0)
			ids.deleteCharAt(ids.length()-1);

		return ids.toString();
	}
	
	//updates an existing good if already present
	public String updateGood(Good good) {
		return db.updateGood(good);
	}
	
	public RequestReturnType removeGood(String objectId) {
		return db.removeGood(objectId);
	}
	
	//	searches and filters the list of all goods bound to the specified name and/or status and page number
	//	key-value example: all goods with 'name :' = name  and/or with 'status :' = status etc.
	public GoodList searchGoods(String name, String status, int pagenumber) {
		return db.searchGoods(name, status, pagenumber);
	}
	
	//Offers
	public OfferList getOffersForGood(String objectId) {
		return db.getOffersForGood(objectId);
	}
	
	public String putOffersForGood(String objectId, OfferList offers) {
		return db.putOffersForGood(objectId, offers);
	}
	
	//TODO: implement only if needed
	public RequestReturnType putOfferForGood(String objectId, Offer offer) {
		//return m.putOffer(objectId, offer);
		return RequestReturnType.MethodNotImplemented;
	}
}
